//Copyright (c) Microsoft Corporation. All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace Microsoft.Office.OneNote
{
	/// <summary>
	/// Locks a node and all of it's children from updates.
	/// </summary>
	public class HierarchySynchronizationLocker : IDisposable
	{
		public enum Mode
		{
			ForRead,
			ForWrite
		};

		public HierarchySynchronizationLocker(HierarchyNode nodeToLock) : this(nodeToLock, Mode.ForRead) { }
		public HierarchySynchronizationLocker(HierarchyNode nodeToLock, Mode lockMode)
		{
			Lock(nodeToLock, lockMode);
		}

		~HierarchySynchronizationLocker()
		{
			Dispose(false);
		}

		protected virtual void Dispose(bool disposing)
		{
			if (!disposed)
			{
				Debug.Assert(disposing, "SynchronizationLocker not property wrapped in a using block.");

				Unlock();

				disposed = true;
			}
		}

		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		public void Lock(HierarchyNode nodeToLock)
		{
			if (disposed)
				throw new ObjectDisposedException(this.GetType().Name);

			Lock(nodeToLock, Mode.ForRead);
		}

		public void Lock(HierarchyNode nodeToLock, Mode lockMode)
		{
			if (disposed)
				throw new ObjectDisposedException(this.GetType().Name);

			Unlock();

			Debug.Assert(!locked);
			Debug.Assert(node == null);

			node = nodeToLock;
			mode = lockMode;

			if (this.mode == Mode.ForRead)
				node.SynchronizationLockedForRead = true;
			else if (this.mode == Mode.ForWrite)
				node.SynchronizationLockedForWrite = true;

			locked = true;
		}

		public void Unlock()
		{
			if (disposed)
				throw new ObjectDisposedException(this.GetType().Name);

			if (locked)
			{
				if (this.mode == Mode.ForRead)
					node.SynchronizationLockedForRead = false;
				else if (this.mode == Mode.ForWrite)
					node.SynchronizationLockedForWrite = false;
			}

			node = null;
		}

		private bool disposed = false;
		private bool locked = false;
		private HierarchyNode node = null;
		private Mode mode = Mode.ForRead;
	}
}
